#include <iostream>
#include <unistd.h>
#include <fcntl.h>
#include <cerrno>
#include <string.h>
#include <sys/select.h>
#include <sys/time.h>
#include <cassert>
#include"selectServer.hpp"
#include<memory>
using namespace std;
//##############################################################################################
// IO=等+数据拷贝
// select 至解决"等"的问题
// 帮用户进行一次等待多个文件sock
// 当哪些文件sock就绪了，select就需要通知用户，对应就绪的sock有哪些，然后用户在调用recv/recvfrom/read等进行数据读取。

//##############################################################################################
// int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout);
// 返回值：就绪的fd的个数--至少有一个fd数据或者空间就绪就可以返回了
// nfds:是需要监视的最大的文件描述符值+1；
// 参数timeout为结构timeval，输入输出型参数，用来设置select()的等待时间--->时间内阻塞，时间到返回，
// 等待时间内如果有fd就绪，timeout就被设置为距离下一次timeout，剩余多少时间
// 中间三个参数都是输入输出型参数
// a.作为输入时：用户告诉内核，你要帮我关心哪个sock的哪一个事件
// b.输出时，内核告诉用户，我所关心的sock中，哪些sock上的哪类事件已经就绪

// fdset文件描述符集---位图结构
// readfds: a.输入时，用户告诉内核，我的比特位中，比特位的位置，表示文件描述符值，比特位的内容表示：是(1)否(0)关心。0000 0101 ，关心0号，2号文件描述符的读事件
//          b.输出时，OS告诉用户，我是OS，用户让我关心的多个fd有结果了，比特位的位置，表示文件描述符值，比特位的内容，表示是(1)否(0)就绪。 0000 0100 ，关心的0描述符没有就绪，2号描述符已就绪，用户可以直接读取，不会阻塞
// 用户和内核都会修改同一个结构，这个参数每一次使用后都需要重新设定
// 其他同理
//##############################################################################################
//select的一般工作模式
// while(true){
// 1.遍历数组，更新出最大值
// 2.遍历数组，调价所有需要关心的fd到 fd_set的位图结构中
// 3.调用select进行事件检测
// 4.遍历数组，找到就绪的事件，根据就绪事件，完成对应的动作
// accept or recv
//}
//##############################################################################################
// select 优缺点
// 优点:---任何一个多路转接方案都具备
// a.效率高 b.应用场景，大量的链接，但是少量的是活跃的---省资源
// 缺点：
// a.为了维护第三方数组，slect 服务器会充满大量的遍历 O(N)
// b.每一次都要对select输出参数进行重新设定
// c.select 能够同时管理的fd个数是有上限的
// d.因为几乎每一个参数都是输入输出型的，select一定会频繁进行用户到内核，内核到用户的参数数据拷贝
// e.编码复杂
//##############################################################################################
void test_time()
{
    while(true)
    {
    cout << "time: " << (unsigned long)time(nullptr) << endl; // 使用time函数获取时间戳
    struct timeval curtime = {0, 0};                          // 使用timeval结构体保存{时间戳秒，微秒}；
    int n = gettimeofday(&curtime, nullptr);
    assert(n == 0);
    (void)n;
    cout << "gettimeofday: " << curtime.tv_sec << "." << curtime.tv_usec << endl;
    sleep(1);
    }
}
int main()
{
    //test_time();
    //fdset fd_set是一个固定大小位图，直接决定了select能同时关心的fd的个数是有上限的
    //能管理1024个文件描述符
    //std::cout << sizeof(fd_set) * 8 << std::endl;
    std::unique_ptr<SelectServer> ser(new SelectServer());
    ser->Start();
    return 0;
}